package park.job;

import org.apache.log4j.Logger;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import jxl.write.Label;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;
import park.record.ConfirmEntryRecord;
import park.record.EntryRecord;
import park.record.ExitRecord;
import park.record.VehicleInfo;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

public class JobExecutor {
	static ArrayList<String> entryIds = new ArrayList<String>();
	private static Logger logger = Logger.getLogger(JobExecutor.class);
	private static Timer jobExecutorMonitor = new Timer();
	// private ExcelOpr excelOpr = ExcelOpr.getInstance();
	private ParkInterfaceCollection parkInterfaceCollection = ParkInterfaceCollection.getInstance();

	public void run(long runTime) throws InterruptedException {
		SchedulerFactory sf = new StdSchedulerFactory();
		Scheduler sched;
		System.getProperties().put("org.quartz.threadPool.threadCount", "10");
		try {
			sched = sf.getScheduler();
			// 开启任务调度监控
			startMonitor(sched);

			EntryRecord entry = ToolUtil.getData(ToolUtil.test_data_path + File.separator + ToolUtil.entryRecordFile,
					EntryRecord.class);
			ExitRecord exit = ToolUtil.getData(ToolUtil.test_data_path + File.separator + ToolUtil.exitRecordFile,
					ExitRecord.class);

			{
				// TODO: 7/4/17 这里面让进站任务先启动并执行一段时间之后再启动出站任务
				addJob(sched, entry, TaskEnterJob.class);
				// 等待一段时间让进站任务制造一定数据进入数据库

				sched.start();
				Thread.sleep(10000);
				// 创建出站任务
				addJob(sched, exit, TaskExitJob.class);
			}

			// 10s间隔查询getParkStatus
			addParkStatusCheckJob(sched);
			Thread.sleep(runTime);
			sched.shutdown();
			try {
				ParkInterfaceCollection.getInstance().getHttpclient().close();
			} catch (IOException e) {
				logger.warn("caught exception", e);
			}
			printConclusion(sched);
		} catch (Exception e) {
			logger.warn("caught exception", e);
		}
	}

	private void startMonitor(Scheduler sched) throws SchedulerException {
		Date runningSince = new Date();
		jobExecutorMonitor.schedule(new TimerTask() {
			long lastCheck = System.currentTimeMillis();
			int executedSinceLastCheck = 0;

			@Override
			public void run() {
				try {
					SchedulerMetaData metaData = sched.getMetaData();
					long temp;
					logger.info("Job Executor had executed " + metaData.getNumberOfJobsExecuted() + " since "
							+ runningSince + " and executed "
							+ (metaData.getNumberOfJobsExecuted() - executedSinceLastCheck) + " since last check"
							+ ", cost " + ((temp = System.currentTimeMillis()) - lastCheck) + "ms");
					lastCheck = temp;
					executedSinceLastCheck = metaData.getNumberOfJobsExecuted();
					addToExcel();

				} catch (SchedulerException e) {
					logger.warn("", e);
				}
			}

		}, 10000, 10000);
	}

	private void addToExcel() {

		ConcurrentHashMap<String, String> map = parkInterfaceCollection.getRecordMap();
		logger.info("map size is :" + map.size());
		Iterator<String> it = map.keySet().iterator();

		while (it.hasNext()) {

			String key = it.next();
			String value = map.get(key);
			String urlName = value.split("_")[0];
			ExcelOpr excel = new ExcelOpr(urlName);
			int count = excel.getWorkbook().getSheet("First Sheet").getRows();
			try {
				excel.addCell(count, urlName, key, value.split("_")[1]);
				excel.writeAndCancle();
			} catch (RowsExceededException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (WriteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// count++;

		}

		parkInterfaceCollection.clearMap();
	}

	private void addJob(Scheduler sched, VehicleInfo info, Class<? extends Job> clazz) throws SchedulerException {
		for (int i = 0; i < 100; i++) {
			// add job
			Date firstRunTime = addOneJob(sched, info, i, clazz);
			logger.info(clazz.getSimpleName() + " job : " + i + " scheduled at :" + firstRunTime);
		}
	}

	private void addParkStatusCheckJob(Scheduler sched) throws SchedulerException {
		JobDetail jobSche = JobBuilder.newJob(SchedJob.class).withIdentity("SchedJob", "SchedJobgroup").build();
		SimpleTrigger triggerSche = TriggerBuilder.newTrigger().withIdentity("SchedJobtrigger", "SchedJobgroup")
				.startNow()
				.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever()).build();
		sched.scheduleJob(jobSche, triggerSche);
	}

	private void printConclusion(Scheduler sched) {
		SchedulerMetaData metaData = null;
		try {
			metaData = sched.getMetaData();
			Date runningSince = metaData.getRunningSince();
			logger.info("Job Executor had executed " + metaData.getNumberOfJobsExecuted() + " since " + runningSince
					+ ", cost " + (System.currentTimeMillis() - runningSince.getTime()) + "ms");
		} catch (SchedulerException e) {
			logger.warn("", e);
		}
	}

	private <T extends Job> Date addOneJob(Scheduler sched, VehicleInfo record, int i, Class<T> t)
			throws SchedulerException {
		JobDetail job = JobBuilder.newJob(t)
				.withIdentity(t.getSimpleName() + "_" + i, t.getSimpleName() + "_group_" + i).build();

		SimpleTrigger trigger = TriggerBuilder.newTrigger()
				.withIdentity(t.getSimpleName() + "_trigger_" + i, t.getSimpleName() + "_group_" + i).startNow()
				.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(9).repeatForever()).build();

		Date firstRunTime;
		JobDataMap jobMap = job.getJobDataMap();

		record.setPlateNo(record.getPlateNo() + i);
		record.setEntryId(String.valueOf(i));
		// record.setEnterTime(System.currentTimeMillis());
		jobMap.put(record.name(), record);
		entryIds.add(String.valueOf(i));

		firstRunTime = sched.scheduleJob(job, trigger);
		return firstRunTime;
	}
}
